package com.school.information.utils;

import cn.hutool.core.convert.Convert;
import com.school.information.core.constant.TokenConstant;
import com.school.information.core.exception.ServiceException;
import com.school.information.enums.result.JwtResultEnum;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/*
 * JWT 工具类
 */
@Slf4j
public class JwtUtil {

    /**
     * 密钥 动态生成的密钥
     * 更强的密钥,JDK11起才能用
     * KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
     * PrivateKey key1 =  keyPair.getPrivate();  // 私钥
     * PublicKey key2 =  keyPair.getPublic();  //公钥
     */
    public static final SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

    /**
     * 自定义头部信息 创建jwt 生成token
     *
     * @param header
     * @param claims
     * @return
     */
    public static String createJwt(Map<String, Object> header, Map<String, Object> claims, Integer expireTime) {
        Date dateNow = new Date();
        Date expireDate = getExpireDate(dateNow);
        if (null != expireTime && expireTime > 0) {
            expireDate = getExpireDate(dateNow, expireTime);
        }
        return Jwts.builder()
                // 设置id
                .setId(RandomIdUtil.getUUID())
                // 设置头部信息
                .setHeader(header)
                // 设置主题
                .setSubject(TokenConstant.SYSTEM_NAME)
                // 设置发送方
                .setIssuer(TokenConstant.SYSTEM_NAME)
                // 自定义内容 如用户的一些信息
                .setClaims(claims)
                // 设置签发日期
                .setIssuedAt(dateNow)
                // 设置过期日期
                .setExpiration(expireDate)
                // 签名算法
                .signWith(key)
                .compact();
    }

    /**
     * 使用默认头部信息 创建jwt 生成token
     *
     * @param claims
     * @return
     */
    public static String createJwt(Map<String, Object> claims) {
        Map<String, Object> header = new HashMap<>(2);
        // 头部信息 可有可无
        header.put("typ", "jwt");
        header.put("alg", "HS256");
        return createJwt(header, claims, -1);
    }

    public static String createJwt(Map<String, Object> claims, Integer expireTime) {
        Map<String, Object> header = new HashMap<>(2);
        // 头部信息 可有可无
        header.put("typ", "jwt");
        header.put("alg", "HS256");
        return createJwt(header, claims, expireTime);
    }

    /**
     * 校验是不是jwt签名
     * true 是jwt签名
     *
     * @param token
     * @return
     */
    public static boolean isSigned(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().isSigned(token);
    }

    /**
     * 校验签名是否正确
     * true 签名正确
     *
     * @param token
     * @return
     */
    public static boolean verify(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取载荷 payload 内容信息 即要传入的信息 如用户信息
     * 使用方法：如获取userId  getClaim(token).get("userId");
     *
     * @param token
     * @return
     */
    public static Claims getClaim(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parserBuilder()
                    .setSigningKey(key).build()
                    .parseClaimsJws(token).getBody();
        } catch (Exception e) {
            throw new ServiceException(JwtResultEnum.JWT_GET_CLAIM_ERROR);
        }
        return claims;
    }

    /**
     * 根据身份信息获取键值
     *
     * @param claims 身份信息
     * @param key    键
     * @return 值
     */
    public static String getValue(Claims claims, String key) {
        return Convert.toStr(claims.get(key), "");
    }

    /**
     * 根据身份获取相应键值信息
     *
     * @param token
     * @param key
     * @return
     */
    public static String getValue(String token, String key) {
        Claims claims = getClaim(token);
        return getValue(claims, key);
    }

    /**
     * 获取头部信息map
     * 使用方法  getHeader(token).get("alg");
     *
     * @param token
     * @return
     */
    public static JwsHeader getHeader(String token) {
        JwsHeader header = null;
        try {
            header = Jwts.parserBuilder().setSigningKey(key)
                    .build().parseClaimsJws(token).getHeader();
        } catch (Exception e) {
            throw new ServiceException(JwtResultEnum.JWT_GET_HEADER_ERROR);
        }
        return header;
    }

    /**
     * 获取jwt发布时间
     *
     * @param token
     * @return
     */
    public static Date getIssuedAt(String token) {
        return getClaim(token).getIssuedAt();
    }

    /**
     * 获取jwt失效时间
     *
     * @param token
     * @return
     */
    public static Date getExpiration(String token) {
        return getClaim(token).getExpiration();
    }

    /**
     * 验证token是否失效 过期
     *
     * @param token
     * @return true:过期  false: 未过期
     */
    public static boolean isExpired(String token) {
        try {
            final Date expiration = getExpiration(token);
            return expiration.before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }

    /**
     * 直接Base64解密获取header内容
     *
     * @param token
     * @return
     */
    public static String getHeaderByBase64(String token) {
        String header = null;
        if (isSigned(token)) {
            try {
                byte[] header_byte = Base64.getDecoder().decode(token.split("\\.")[0]);
                header = new String(header_byte);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return header;
    }

    /**
     * 直接Base64解密获取payload内容
     *
     * @param token
     * @return
     */
    public static String getPayloadByBase64(String token) {
        String payload = null;
        if (isSigned(token)) {
            try {
                byte[] payload_byte = Base64.getDecoder().decode(token.split("\\.")[1]);
                payload = new String(payload_byte);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return payload;
    }

    /**
     * 设置jwt过期时间
     *
     * @param now
     * @return
     */
    public static Date getExpireDate(Date now) {
        return new Date(System.currentTimeMillis() + TokenConstant.EXPIRES_IN * 1000);
    }

    public static Date getExpireDate(Date now, Integer expireTime) {
        return new Date(System.currentTimeMillis() + expireTime * 1000);
    }


//    public static void main(String[] args) throws Exception {
//        //用户自定义信息claims
//        Map<String, Object> map = new HashMap<>();
//        map.put("userId", "test122");
//        String token = createJwt(map);
//        System.out.println(token);
//
//        System.out.println("claim:" + getClaim(token).get("userId"));
//        System.out.println("header:" + getHeader(token));
//        //    System.out.println(getIssuedAt(token));
//        Claims claims = getClaim(token);
//
//        System.out.println(isSigned(token));
//        System.out.println(verify(token));
//        System.out.println(getHeaderByBase64(token));
//        System.out.println(getPayloadByBase64(token));
//
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
//        System.out.println("签发时间:" + sdf.format(claims.getIssuedAt()));
//        System.out.println("过期时间:" + sdf.format(claims.getExpiration()));
//        System.out.println("当前时间:" + sdf.format(new Date()));
//
//        System.out.println("判断签名是否过期");
//        System.out.println(isExpired(token));
//
//        Thread.sleep(20000);
//        System.out.println(isExpired(token));
//
//    }
}